#pragma once
#include <cmath>
#include <iostream>
#include <string>


class IntPair {
public:
    IntPair(int a, int b) : numA(a), numB(b) {}
    ~IntPair() {};

    void print() {
        std::cout << "pair (" << numA << ", " << numB << ")" << '\n';
    }

    bool isEqual(IntPair& pIn) {
        return pIn.numA == numA && pIn.numB == numB;
    }

private:
    int numA;
    int numB;
};


class Date {
public:
    Date(int year, int month, int day, int hour, int min, int sec)
        : _year(year), _month(month), _day(day), _hour(hour), _min(min), _sec(sec) {}

    ~Date() {}

    void incrementYear()    { ++_year; }
    void incrementMonth()   { ++_month; }
    void incrementDay()     { ++_day; }
    void incrementHour()    { ++_hour; }
    void incrementMinute()  { ++_min; }
    void incrementSecond()  { ++_sec; }

    void setDateParamYear(int year)     { _year = year; }
    void setDateParamMonth(int month)   { _month = month; }
    void setDateParamDay(int day)       { _day = day; }
    void setDateParamHour(int hour)     { _hour = hour; }
    void setDateParamMinute(int minute) { _min = minute; }
    void setDateParamSecond(int second) { _sec = second; }

    // const func
    int getDateParamYear() const    { return _year; }
    int getDateParamMonth() const   { return _month; }
    int getDateParamDay() const     { return _day; }
    int getDateParamHour() const    { return _hour; }
    int getDateParamMinute() const  { return _min; }
    int getDateParamSecond() const  { return _sec; }

    void print() const {
        std::cout << " [Date] [Year-Month-Day Hour:Minute:Second] " << _year << "-" << _month << "-" << _day << " " << _hour << ":" << _min << ":" << _sec << '\n';
    }

private:
    int _year {};
    int _month {};
    int _day {};
    int _hour {};
    int _min {};
    int _sec {};
};


enum class Sex {
    SEX_MEALE,
    SEX_FEMALE,
};


class Person {
    // Suggestion: 尽量使用private + getter/setter.
protected:
    std::string name;
    int age;
    Sex sex;

public:
    Person(const std::string& n, int a, Sex s)
        : name(n), age(a), sex(s) {}

    void display() const {
        std:: cout << " Name: " << name << ", Age: " << age << ", Sex(0: Male, 1: Female): " << (int)sex << '\n';
    }
};


class Student : public Person {
public:
    Student(const std::string& n, int a, Sex s, int index)
        : Person(n, a, s), mIndex(index) {}

    void print() const {
        std::cout << " Student: " << name << ", Sex(0: Male, 1: Female): " << (int)sex << ", Age: " << age << ", Index(NumberID): " << mIndex << '\n';
    }

private:
    int mIndex;
};


class Ball {
public:
    Ball()
        : mColor {"white"}
        , mRadius {1.0f} {}

    Ball(const std::string& color="black", float rad = 10.0f)
        : mColor(color)
        , mRadius(rad) {}

    const std::string& getColor() const { return mColor; }
    float getRadius() const { return mRadius; }

private:
    std::string mColor;
    float       mRadius;
};


class Foo {
public:
    explicit Foo()
        : mTestStr{"HaHaHa"}
        , mNumber(520) {}

    explicit Foo(std::string& s, int num)
        : mTestStr(s)
        , mNumber(num) {}

    int getNumber() const { return mNumber; }
    const std::string& getString() const { return mTestStr; }

private:
    std::string mTestStr;
    int         mNumber;
};


class PairNew {
public:
    constexpr PairNew(int x, int y)
        : mX{x}
        , mY{y} {}

    constexpr int greater() const {
        return (mX > mY ? mX : mY);
    }

    constexpr void reset() {
        mX = 0;
        mY = 0;
    }

    constexpr const int& getX() const { return mX; }
    constexpr const int& getY() const { return mY; }

    void display() const {
        std::cout << " X: " << getX() << ", Y: " << getY() << '\n';
     }

private:
    int mX;
    int mY;

};


class Point2d {
public:
    Point2d() = default;
    Point2d(double x, double y)
        : m_x(x)
        , m_y(y) {
            print();
        }

    ~Point2d() {}

    void print() const {
        std::cout << "Point2d (" << m_x << ", " << m_y << ")\n";
    }

    double distanceTo(const Point2d& secPoint) {
        return std::sqrt((m_x - secPoint.m_x) * (m_x - secPoint.m_x) + (m_y - secPoint.m_y) * (m_y - secPoint.m_y));
    }

private:
    double      m_x { 0.0 };
    double      m_y { 0.0 };
};


class FractionNew {
public:
    explicit FractionNew(int num, int denom)
        : numerator(num)
        , denominator(denom) { }

    ~FractionNew() {}

    void printFraction() const {
        std::cout << numerator << '/' << denominator << '\n';
    }

    const FractionNew multiply(const FractionNew& f) const {
        return FractionNew( f.numerator * numerator, f.denominator * denominator );
    }

    void getFraction()
    {
        std::cout << "Enter a value for numerator: ";
        std::cin >> numerator; // this is a member function, so we can access members directly
        std::cout << "Enter a value for denominator: ";
        std::cin >> denominator;
        std::cout << '\n';
    }

private:
    int     numerator { 0 };
    int     denominator { 1 };
};


void testHomeWork14_4();
void testConstHomeWork14_5();
void testHomeWork14_10();
void testHomeWork14_16();
void testHomeWork14_17();
